# -*- coding: utf-8 -*-
import os,shutil,time,cPickle
 
import StringIO
import codecs
import xml.dom.minidom as minidom

import pystore.core.framework.os2 as os2
import pystore.core.config as config

class SendError(Exception):
    def __init__(self,mailId, fromFiles,innerError=None):
        self.mailId = mailId
        self.fromFiles = fromFiles
        self.innerError=innerError
    def __str__(self):
        return '%s:%s \n inner error:%s' % (self.mailId,str(self.fromFiles),str(self.innerError)) 
    
class SendErrorDuplicateSubjectAndFile(SendError):
    pass


class _XmlStoreTree(object):
    
    @staticmethod
    def load(xml):
        return _XmlStoreTree(minidom.parseString(xml))
    
    def __init__(self,dom=None):
        if dom==None:
            self.dom = minidom.getDOMImplementation().createDocument(None, 'pystore', None)
        else:
            self.dom = dom
            
        self.__root =  self.dom.documentElement
        
    def appendDirElement(self,path):
        """return path last node xmlElement"""
        names=os2.splitPath(path)
        parent=self.__root
        for childName in names:
            if not self.__hasChild(parent,childName):
                self.__addDir(parent,childName)
            parent=self.__getChild(parent,childName)
        return parent
    
    def removeElement(self,path):
        element=self.__getElement(path)
        element.parentNode.removeChild(element)
        
    def getFileData(self,path):
        """
        return os2.FileData
        """
        fileElement=self.__getElement(path)
        result=os2.FileData()
        result.name      =fileElement.getAttribute('name')
        result.id        =fileElement.getAttribute('id')
        result.size      =int(fileElement.getAttribute('size'))
        result.modifyTime=int(fileElement.getAttribute('modifyTime'))
        return result
    
    def isfile(self,path):
        assert self.exists(path)
        if self.__getElement(path).nodeName=='file':
           return True
        return False
    
    def isdir(self,path):
        return not self.isfile(path)
        
    def listdir(self,path):
        element=self.__getElement(path)
        return [childElement.getAttribute('name') for childElement in self.__getChildrenElements(element)]
    
    def exists(self,path):
        return  self.__getElement(path)!=None

    def appendFileElement(self,path,fileData):
        dir=os.path.split(path)[0]
        lastDirElement=self.appendDirElement(dir)
        fileElement=self.dom.createElement('file')
        fileElement.setAttribute('name',fileData.name)
        fileElement.setAttribute('id',fileData.id)
        fileElement.setAttribute('size',str(fileData.size))
        fileElement.setAttribute('modifyTime',str(fileData.modifyTime))
        lastDirElement.appendChild(fileElement)
        
    def setFileData(self,path,fileData):
        fileElement=self.__getElement(path)
        fileElement.setAttribute('name',fileData.name)
        fileElement.setAttribute('id',fileData.id)
        fileElement.setAttribute('size',str(fileData.size))
        fileElement.setAttribute('modifyTime',str(fileData.modifyTime))
        
    def walk(self,walkCallback,path):
        walkCallback(path)
        if self.isfile(path):
            return
        for childName in self.listdir(path):
            childPath=os.path.join(path,childName)
            self.walk(walkCallback,childPath)
    
    def __getElement(self,path):
        names=os2.splitPath(path)
        resultElement=self.__root
        for childName in names:
            if resultElement==None:
                return None
            resultElement=self.__getChild(resultElement,childName)
        return resultElement
    
    def __addDir(self,parent,childName):
        assert not self.__hasChild(parent,childName)
        child=self.dom.createElement('dir')
        child.setAttribute('name',childName)
        parent.appendChild(child)
         
    def __getChild(self,parent,childName):
        for child in self.__getChildrenElements(parent):
            if child.getAttribute('name')==childName:
                return child 
        return None
    
    def __getChildrenElements(self,parent):
        result=[]
        for node in parent.childNodes:
            if node.nodeType == node.ELEMENT_NODE:
                result.append(node)
        return result
    
    def __hasChild(self,parent,childName):
        if self.__getChild(parent,childName) != None:
            return True
        return False
    
    def writexml(self,filePath):
        dir=os.path.split(filePath)[0]# os.path.split return -> 0: dir 1:filename
        if dir!='' and not os.path.exists(dir) :
            os.makedirs(dir) 
        f=file(filePath, 'wb+')
        writer = codecs.lookup('utf-8')[3](f)
        self.dom.writexml(writer, encoding='utf-8')
        writer.close()

class RemoteFileNotExistException(Exception):
    def __init__(self,path):
        self.path = path
        
    def __str__(self):
        return 'file(%s) not exists' % (self.path) 

class UploadError(Exception):
    def __init__(self,filename,innerError):
        self.filename=filename
    def __str__(self):
        return '%s:%s \n inner error:%s' % (self.filename,str(self.innerError)) 



class DiskRemoteService(object):
    def __init__(self,storePath):
        self.storePath=storePath
    
    def getLocation(self):
        return self.storePath
    
class RemoteService(object):
    treeId='pyStore_tree_id'
    
    def __init__(self,mailClient):
        self.__mailClient=mailClient
        self.__tree=None
        #todo load 不在初始化时用，要单独调用
        self.load()
        
    def load(self):
        def walkMethod(content):
            try:
                self.__tree=_XmlStoreTree.load(content)
                return False
            except Exception,e:
                print e #todo logging
                print content
                return True
            
        self.__mailClient.walkFirstAttachment(walkMethod)
        #no load from mail box ,create new tree
        if self.__tree==None:
            self.__tree=_XmlStoreTree()

    def makedirs(self,path):
        self.__tree.appendDirElement(path)
        self.__uploadTree()
        
    def removeFile(self,path):
        assert self.isfile(path)
        id=self.getFileData(path).id
        self.__tree.removeElement(path)
        self.__mailClient.removeAll(id)
        self.__uploadTree()
        
    def rmdir(self,path):
        assert self.isdir(path)
        needRemoveFiles=[]
        def walkFuc(walkPath):
            if (self.isfile(walkPath)):
                needRemoveFiles.append(walkPath)
        
        self.__tree.walk(walkFuc,path)
        map(self.removeFile,needRemoveFiles)
        self.__tree.removeElement(path)
        self.__uploadTree()
    
    def isdir(self,path):
        return self.__tree.isdir(path)
        
    def isfile(self,path):
        return self.__tree.isfile(path)
    
    def exists(self,path):
        return self.__tree.exists(path)
        

    def getFileData(self,path):
        if not self.exists(path):
            raise RemoteFileNotExistException(path)
        return self.__tree.getFileData(path)
        

    def listdir(self,path):
        return self.__tree.listdir(path)
    
    def walk(self,walkCallback,path):
        """
        walkCallback -> func(path)
        
        """
        self.__tree.walk(walkCallback,path)
        
    def createFile(self,toPath,content):
        assert content !=None
        filename=os.path.split(toPath)[1]
        localPath=os2.createTempFile(filename,content)
        self.uploadFile(localPath,toPath)
    
    def uploadFile(self,fromLocalPath,toPath):
        data=os2.getFileData(fromLocalPath)
        data.name=os.path.split(toPath)[1]#use toPath fileName
        
        if self.__tree.exists(toPath):
            self.removeFile(toPath)
        self.__tree.appendFileElement(toPath, data)
        try:
            self.__send(data.id,fromLocalPath)
        except SendError ,e:
            raise UploadError(fromLocalPath,e)

    def downloadFile(self,toLocalPath,fromPath):
        fileData=self.getFileData(fromPath)
        id=fileData.id
        content =self.__mailClient.readFromIndex(fileData.id,1)
        os2.createFile(toLocalPath,content,fileData.modifyTime)


    def __send(self,id,filePath):
        """
        str filePath  if filePath is None, only send xmltree
        """
        try:
            filenames= [self.__saveTree()]
            filePath and filenames.append(filePath)
            self.__mailClient.send(id,filenames)
        except SendError ,e:
            raise UploadError(filePath,e)
        
    def __saveTree(self):
        """return save path"""
        #本来是用os2.createFile()的，但如有中文自字符在tree.xml中就会有如下的异常,编码问题还是没搞清楚
#  File "D:\projects\pystore\pystore\os2.py", line 38, in createFile
#    f.write( fileContent )
#__builtin__.UnicodeEncodeError: 'ascii' codec can't encode characters in position 671-677: ordinal not in range(128)

        #现在用dom.writexml就没啥问题了
        self.__tree.writexml(self.__treeTempPath())
        return self.__treeTempPath()
        
    def __uploadTree(self):
        self.__mailClient.send(RemoteService.treeId,[self.__saveTree()])
        
    def __treeTempPath(self):
        temp=os.path.join(config._workspacePath,'temp')
        return os.path.join(temp,'tree.xml')

